#!/usr/bin/env python
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# I use the following naming convention:
# - joined_lower for method or function or variables
# - JOINED_UPPER for global constants, such as IDs
# - UpperCamelCase for class names

import os
import sys
import imghdr
import urllib

import threading

import wx

import sitesel
import archive
import resources
import imageviewer
import optionsdialog as optdialog

try:
    from cStringIO import StringIO as _SIO
except ImportError:
    from StringIO import StringIO as _SIO

__version__ = "1.6.2"

ID_MENU_OPEN_URL = wx.NewId()
ID_MENU_OPEN_FILE = wx.NewId()
ID_MENU_OPEN_ZIP = wx.NewId()

ID_OPEN_FILE = wx.NewId()
ID_OPEN_URL = wx.NewId()
ID_PREVIOUS = wx.NewId()
ID_NEXT = wx.NewId()
ID_DOWN_ALL = wx.NewId()
ID_REMOVE_QUEUE = wx.NewId()

ID_SAVE_IN_ZIP = wx.NewId()
ID_SAVE_IN_DIR = wx.NewId()
ID_DONT_SAVE = wx.NewId()

ID_FIT_WINDOW = wx.NewId()
ID_ZOOM_DECREASE = wx.NewId() 
ID_ZOOM_INCREASE = wx.NewId()

ID_OPTIONS = wx.NewId()

ID_SHOW_SHORT = wx.NewId()
ID_ABOUT = wx.NewId()

NO_MANGA_ERROR = resources.NO_MANGA_ERROR
ERROR_CAPTION = resources.ERROR_CAPTION
ERROR_STYLE = resources.ERROR_STYLE

MAX = 15

class MyOpener(urllib.FancyURLopener):
    version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11'

#create an opener, so we can change its user-agent
urlopen = MyOpener().open
urlretrieve = MyOpener().retrieve

imgpath = lambda x: os.path.join("pixmaps", x)

class MReader(wx.Frame):
    def __init__(self, title, *args):
        #print sys._getframe().f_code.co_name
        wx.Frame.__init__(self, None, -1, title, *args)
        #self.name = ""
        self.where = "."
        self.site = ""
        self.filelist = []
        self.completed = {}
        #tbicon = wx.TaskBarIcon()
        icon = wx.Icon(imgpath("beta_logo.ico"), wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)
        if resources.get_preferences("Interfaccia").get("show tray", False):
            self.tbicon = wx.TaskBarIcon()
            self.tbicon.SetIcon(icon, "ManWer %s - Running" %__version__)
        self.local_file = 0 #has 3 value: 0 if the image is given over the internet, 1 for a local dir and 
                            #2 if the file is a zip
        self.n = -1
        self.backcolour = resources.get_preferences("Interfaccia").get('colour', (0, 0, 0))
        self.dc = imageviewer.ImageWindow(self)
        self.img_zoom_percent = 0
        self.zip_path = ""
        self.save = 1 #0 = do not save
                      #1 = Save in a dir (specified in self.where)
                      #2 = save in a zip (self.zip_path)
        self.manga = None #the module to use to download the images
        
        self.image = None #self.image is the image displayed in the ImageWindow
        #MENUBAR
        menubar = wx.MenuBar()
        #FILE 
        file = wx.Menu()
        #FILE > OPEN
        open = wx.Menu()
        #FILE > OPEN > FROM URL
        from_url = wx.MenuItem(open, ID_MENU_OPEN_URL, "Da URL")
        open.AppendItem(from_url)
        #FILE > OPEN > FROM FILE
        from_dir = wx.MenuItem(open, ID_MENU_OPEN_FILE, "Da file locale")
        open.AppendItem(from_dir)
        #FILE > OPEN > FROM ZIP
        from_zip = wx.MenuItem(open, ID_MENU_OPEN_ZIP, "Da file Zip")
        open.AppendItem(from_zip)
        
        file.AppendMenu(0, "Apri", open)
        #FILE > QUIT
        quit = wx.MenuItem(file, 2, "Chiudi")
        
        file.AppendItem(quit)
        #EDIT
        edit = wx.Menu()
        #EDIT > SAVE IN...
        save_in = wx.Menu()
        #EDIT > SAVE IN > DIR
        dir = wx.MenuItem(save_in, ID_SAVE_IN_DIR, "cartella", kind=wx.ITEM_RADIO)
        #EDIT > SAVE IN > ZIP
        zip = wx.MenuItem(save_in, ID_SAVE_IN_ZIP, "zip", kind=wx.ITEM_RADIO)
        #EDIT > SAVE IN > DON'T SAVE
        temp = wx.MenuItem(save_in, ID_DONT_SAVE, "non salvare", kind=wx.ITEM_RADIO)

        save_in.AppendItem(dir)
        save_in.AppendItem(zip)
        save_in.AppendItem(temp)
        
        #EDIT > ZOOM
        zoom = wx.Menu()
        #EDIT > ZOOM > FIT TO WINDOW SIZE
        fit_window = wx.MenuItem(zoom, ID_FIT_WINDOW, "Adatta a dimensioni")
        #EDIT > ZOOM > INCREASE
        increase_zoom = wx.MenuItem(zoom, ID_ZOOM_INCREASE, "Aumenta")
        decrease_zoom = wx.MenuItem(zoom, ID_ZOOM_DECREASE, "Diminuisci")

        zoom.AppendItem(increase_zoom)
        zoom.AppendItem(decrease_zoom)
        zoom.AppendItem(fit_window)

        edit.AppendMenu(3, "Zoom", zoom)
        edit.AppendMenu(4, "Salva in", save_in)
        
        #SETTINGS
        settings = wx.Menu()
        #SETTINGS > OPTIONS
        options = wx.MenuItem(settings, ID_OPTIONS, "Opzioni")
        
        settings.AppendItem(options)
        
        #HELP
        help = wx.Menu()
        show_shortcut = wx.MenuItem(help, ID_SHOW_SHORT, "Mostra scorciatoie")
        about = wx.MenuItem(help, ID_ABOUT, "About")
        
        help.AppendItem(show_shortcut)
        help.AppendItem(about)
        
        #add the menu to the menubar
        menubar.Append(file, "File")
        menubar.Append(edit, "Modifica")
        menubar.Append(settings, "Strumenti")
        menubar.Append(help, "?")
        self.SetMenuBar(menubar)
        #TOOLBAR
        self.toolbar = resources.MyToolBar(self, -1, style=wx.TB_TEXT|wx.TB_HORIZONTAL|wx.TB_FLAT)
        self.toolbar.AddMany([(ID_OPEN_FILE, "Apri (locale)", wx.Bitmap(imgpath("document-open.png"))),
                            (ID_OPEN_URL, "Apri (URL)", wx.Bitmap(imgpath("folder-remote.png"))),
                            "sep",
                            (ID_PREVIOUS, "Precedente", wx.Bitmap(imgpath("go-previous.png"))),
                            (ID_NEXT, "Successiva", wx.Bitmap(imgpath("go-next.png"))),
                            "sep",
                            (ID_REMOVE_QUEUE, "Rimuovi coda", wx.Bitmap(imgpath("process-stop.png")))])
        self.toolbar.Realize()
        self.SetToolBar(self.toolbar)
        self.toolbar.EnableTool(ID_PREVIOUS, False)
        self.toolbar.EnableTool(ID_NEXT, False)          
        self.toolbar.EnableTool(ID_REMOVE_QUEUE, False)

        #STATUSBAR
        self.statusbar = wx.StatusBar(self, -1)
        self.statusbar.SetFieldsCount(4)
        self.statusbar.SetStatusWidths(self.calculate_percent([30, 20, 30, 20]))
        self.SetStatusBar(self.statusbar)

        #BIND
        self.Bind(wx.EVT_MENU, self.open_from_url, id=ID_MENU_OPEN_URL)
        self.Bind(wx.EVT_MENU, self.open_from_local_file, id=ID_MENU_OPEN_FILE)
        self.Bind(wx.EVT_MENU, self.open_from_zip, id=ID_MENU_OPEN_ZIP)

        self.Bind(wx.EVT_MENU, self.increase_zoom, id=ID_ZOOM_INCREASE)
        self.Bind(wx.EVT_MENU, self.decrease_zoom, id=ID_ZOOM_DECREASE)
        self.Bind(wx.EVT_MENU, self.fit_window_size, id=ID_FIT_WINDOW)
        
        self.Bind(wx.EVT_MENU, self.save_zip, id=ID_SAVE_IN_ZIP)
        self.Bind(wx.EVT_MENU, self.save_in_dir, id=ID_SAVE_IN_DIR)
        self.Bind(wx.EVT_MENU, self.dont_save, id=ID_DONT_SAVE)
        
        self.Bind(wx.EVT_MENU, self.show_options, id=ID_OPTIONS)
        
        self.Bind(wx.EVT_MENU, self.show_key_bindings, id=ID_SHOW_SHORT)
        self.Bind(wx.EVT_MENU, self.show_about, id=ID_ABOUT)

        self.Bind(wx.EVT_TOOL, self.open_dir, id=ID_OPEN_FILE)
        self.Bind(wx.EVT_TOOL, self.open_url, id=ID_OPEN_URL)
        self.Bind(wx.EVT_TOOL, self.on_next, id=ID_NEXT)
        self.Bind(wx.EVT_TOOL, self.on_previous, id=ID_PREVIOUS)
        self.Bind(wx.EVT_TOOL, self.on_remove_queue, id=ID_REMOVE_QUEUE)
        #self.Bind(wx.EVT_TOOL, self.on_download_all, id=ID_DOWN_ALL)
        
        self.Bind(wx.EVT_CLOSE, self.on_close)
    
    def on_remove_queue(self, event):
        self.n = -1
        self.filelist = []
        self.dc.clear_dc()
        self.SetStatusText("", 0)
        self.SetStatusText("", 1)
        self.SetStatusText("", 2)
        self.toolbar.EnableTool(ID_PREVIOUS, False)
        self.toolbar.EnableTool(ID_NEXT, False)
        self.toolbar.EnableTool(ID_REMOVE_QUEUE, False)
    
    def enable_menu(self, index, subindex, enable):
        menu = self.GetMenuBar()
        menu.GetMenu(index).FindItemByPosition(subindex).Enable(enable)
        
    def refresh(self):
        self.dc.change_colour(self.backcolour)
        
    def change_background_colour(self, colour):
        self.dc.SetBackgroundColour(colour)
        self.dc.Refresh()
        self.backcolour = colour
    
    def dont_save(self, event):
        self.save = 0
        
    def show_options(self, event):
        #let's show a little options dialog
        optdialog.OptionDialog(self, "Manwer | Opzioni")
    
    def on_close(self, event):
        #print sys._getframe().f_code.co_name
        self.Destroy()
    
    def save_zip(self, event):
        #print sys._getframe().f_code.co_name
        #print "save_zip"
        a = resources.CreateZipDialog(self, 'Crea ZIP')
        a.ShowModal()
    
    def save_in_dir(self, event):
        #print sys._getframe().f_code.co_name
        dird = wx.DirDialog(self, "Scegli dove salvare...")
        if dird.ShowModal() == wx.ID_OK:
            self.where = dird.GetPath()
            self.save = 1
    
    def open_from_zip(self, event):
        #print sys._getframe().f_code.co_name
        filed = wx.FileDialog(self, "Scegli un file", wildcard="File ZIP (*.zip) | *.zip")
        self.enable_menu(1, 1, True)
        if filed.ShowModal() == wx.ID_OK:
            self.local_file = 2
            path = filed.GetPath()
            self.filelist = [os.path.join(path, el) for el in archive.listdir(path) 
                            if archive.isfile(path + ':' + el)]
            self.filelist.sort()
            self.path = path
            self.n = -1
            self.enable_menu(1, 1, False)
            self.toolbar.EnableTool(ID_NEXT, True)
            self.toolbar.EnableTool(ID_REMOVE_QUEUE, True)
        else:
            self.local_file = 0
    
    def show_about(self, event):
        #print sys._getframe().f_code.co_name
        about_dialog = resources.HtmlMessageDialog(self, "ManWer | About", "about.html")
        about_dialog.ShowModal()
    
    def show_key_bindings(self, event):
        #print sys._getframe().f_code.co_name
        key_dialog = resources.HtmlMessageDialog(self, "ManWer | Scorciatoie", "key_bindings.html")
        key_dialog.ShowModal()
    
    def fit_window_size(self, event):
        #print sys._getframe().f_code.co_name
        self.dc.fit_window()
        self.dc.Refresh()
    
    def open_from_url(self, event):
        #print sys._getframe().f_code.co_name
        self.open_url(event)
        
    def open_from_local_file(self, event):
        #print sys._getframe().f_code.co_name
        self.open_dir(event)

    def increase_zoom(self, event):
        #print sys._getframe().f_code.co_name
        try:
            self.dc.zoom_in()
            self.img_zoom_percent = self.dc.to_zoom
        except AttributeError:
            wx.MessageDialog(self, NO_MANGA_ERROR, ERROR_CAPTION, style=ERROR_STYLE).ShowModal()
    
    def decrease_zoom(self, event):
        #print sys._getframe().f_code.co_name
        try:
            self.dc.zoom_out()
            self.img_zoom_percent = self.dc.to_zoom
        except AttributeError:
            wx.MessageDialog(self, NO_MANGA_ERROR, ERROR_CAPTION, style=ERROR_STYLE).ShowModal()

    def open_dir(self, event):  
        dlg = wx.DirDialog(self, "Scegli una cartella...", style=wx.DD_DEFAULT_STYLE)
        if dlg.ShowModal() == wx.ID_OK:
            self.local_file = 1
            path = dlg.GetPath()
            self.filelist = [os.path.join(path, el) for el in os.listdir(path)
                            if os.path.isfile(os.path.join(path, el)) and
                            imghdr.what(os.path.join(path, el)) is not None]
            #print self.filelist
            self.filelist.sort()
            self.n = -1
            self.enable_menu(1, 1, False)
            self.toolbar.EnableTool(ID_NEXT, True)
            self.toolbar.EnableTool(ID_REMOVE_QUEUE, True)
        else:
            self.local_file = 0
        dlg.Destroy()
    
    def open_url(self, event):
        #print sys._getframe().f_code.co_name
        self.enable_menu(1, 1, True)
        a = sitesel.SiteSelector(self, 'Seleziona backend')
        a.ShowModal()
        self.manga = sitesel.get_manga(a)
        self.completed = {}
        try:
            t = '@ ' + str(self.manga).split()[1].split('.')[-1].strip('\'')
        except IndexError:
            t = ""
        else:
            self.toolbar.EnableTool(ID_NEXT, True)
            self.toolbar.EnableTool(ID_REMOVE_QUEUE, True)
            self.SetTitle('ManWer %s %s'%(__version__, t))
            self.local_file = 0
            self.n = -1

    def on_size(self):
        #print sys._getframe().f_code.co_name
        #when the frame inits it has no statusbar, but occours a on_size event, raising an exception
        try:
            self.statusbar.SetStatusWidths(self.calculate_percent([30, 20, 30, 20]))
        except AttributeError:
            pass
    
    def calculate_percent(self, percent):
        #print sys._getframe().f_code.co_name
        w = self.GetSizeTuple()[0]
        return [(i*w)/100 for i in percent]
    
    def on_download_all(self, event):
        #print sys._getframe().f_code.co_name
        self.toolbar.EnableTool(ID_DOWN_ALL, False)
        self.downloadAll()

    def on_next(self, event):
        #print sys._getframe().f_code.co_name
        self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
        self.n += 1
        if self.n > 0:
            self.toolbar.EnableTool(ID_PREVIOUS, True)
        if not self.local_file:
            self.toolbar.EnableTool(ID_NEXT, False)
            threading.Thread(target=self.download).start()
        else: #images are in a directory or in a zip
            if self.n >= len(self.filelist):
                wx.MessageDialog(self, "Non ci sono altre immagini", ERROR_CAPTION, style=ERROR_STYLE).ShowModal()
                self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
                self.toolbar.EnableTool(ID_NEXT, False)
                self.n -= 1
                return
            elif self.local_file == 1:
                self.image = wx.Image(self.filelist[self.n])
            else: #images are in a zip
                self.image = wx.ImageFromStream(archive.get_file(self.path, self.n))
            self.SetStatusText("Immagine: %s/%s" %(self.n+1, len(self.filelist)), 1)
            self.paint()
        self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

    def on_previous(self, event):
        #print sys._getframe().f_code.co_name
        self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
        self.n -= 1
        if self.n < 0:
            wx.MessageDialog(self, "Non ci sono altre immagini", ERROR_CAPTION, style=ERROR_STYLE).ShowModal()
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
            self.toolbar.EnableTool(ID_PREVIOUS, False)
            self.n += 1
            return
        if not self.local_file:
            self.toolbar.EnableTool(ID_PREVIOUS, False)
            threading.Thread(target=self.download).start()
        else:
            if self.local_file == 1:
                self.image = wx.Image(self.filelist[self.n])
            else:
                self.image = wx.ImageFromStream(archive.get_file(self.path, self.n))
            self.SetStatusText("Immagine: %s/%s" %(self.n+1, len(self.filelist)), 1)
            self.paint()
        self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

    def download(self):
        #print sys._getframe().f_code.co_name
        self.url, path, name = self.manga.get_img_url(self.site, self.n)
        self.statusbar.SetStatusText('Scaricando %s da %s...' %(name, self.site), 0)
        img_path = os.path.join(self.where, path, name)
        if img_path in self.completed:
            self.image = wx.ImageFromStream(self.completed[img_path])
        else:
            page = urlopen(self.url)
            meta = page.info()
            data = page.read()
            self.completed[img_path] = _SIO(data)
            #print "size: %f kb" %(len(self.completed), self.n, float(meta.getheaders("Content-Length")[0]) / 1024)
            if len(self.completed) >= MAX:
                self.completed = {}
            if self.save == 1: #in a dir
                path = os.path.join(self.where, path)
                if not os.path.exists(path):
                    os.makedirs(path)
                f = open(img_path, 'wb')
                f.write(data)
            if self.save == 2: #in a zip
                if not os.path.splitext(self.zip_path)[1] == '.zip':
                    self.zip_path += ' - '.join(path.split(os.sep)) + '.zip'
                archive.save_file(self.zip_path, name, data)
            #if save is 0 we already have the image in data stream.
            self.image = wx.ImageFromStream(_SIO(data))
        self.paint()
        self.statusbar.SetStatusText('Fatto', 0)
    
    def paint(self):
        #print sys._getframe().f_code.co_name
        #self.dc = ImageWindow(self, wx.Image(self.path))
        img = self.image
        try:
            string = str(img.GetWidth()) + ', ' + str(img.GetHeight())
        except wx.PyAssertionError:
            return
        finally:
            self.toolbar.EnableTool(ID_NEXT, True)
            self.toolbar.EnableTool(ID_PREVIOUS, True)
        self.dc.change_image(img)
        self.statusbar.SetStatusText(string, 3)
        del img
        #self.toolbar.EnableTool(ID_DOWN_ALL, True)